package com.ybg.dao;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.JdbcUtils;

import com.ybg.utils.YbgException;

/**
 * DAO工具抽象类。所有DAO类皆继承与该类。
 *
 * @author ningjh
 * @since  2016-07-20
 */
public abstract class AbstractDao {
    @Autowired
    @Qualifier("jdbcTemplate")
    protected JdbcTemplate jdbcTemplate;

    /**
     * 保存对象到数据库
     * 
     * @param t 对象实例
     * @return
     * @throws Exception
     */
    protected <T> boolean save(T t) throws Exception {
    	StringBuilder sql   = new StringBuilder("insert into ");
    	StringBuilder val   = new StringBuilder("values(");
    	Class<?>      clazz = t.getClass();
    	
    	// 获得实体类的Table注解，用来确定数据库表
    	Table table = clazz.getAnnotation(Table.class);
    	
    	if(table == null) {
    		throw new YbgException("0002");
    	} else {
    		sql.append(table.name()).append("(");
    	}
    	
    	List<Object> values = new ArrayList<Object>();
    	Field[]      fields = t.getClass().getDeclaredFields();
    	
    	// 装拼需要保存的字段(有Column注解的字段才保存)
    	// 暂不对主键做处理，用户需要保证主键ID大于0
    	for(int i = 0; i < fields.length; i++) {
    		Column column = fields[i].getAnnotation(Column.class);
    		
    		if(column != null) {
    			sql.append(column.name());
    			val.append("?");
    			
    			values.add(clazz.getMethod("get" + StringUtils.capitalize(fields[i].getName())).invoke(t));
    			
    			if(i < (fields.length - 1)) {
    				sql.append(", ");
    				val.append(", ");
    			}
    		}
    	}
    	
    	if(values.size() == 0) {
    		throw new YbgException("0003");
    	}
    	
    	sql.append(") ").append(val.append(")"));
    	
    	return jdbcTemplate.update(sql.toString(), values.toArray()) == 1;
    }
    
    /**
     * 根据主键更新对象到数据库
     * 
     * @param t 对象实例
     * @return
     * @throws Exception
     */
    protected <T> boolean update(T t) throws Exception {
    	StringBuilder sql   = new StringBuilder("update ");
    	Class<?>      clazz = t.getClass();
    	
    	// 获得实体类的Table注解，用来确定数据库表
    	Table table = clazz.getAnnotation(Table.class);
    	
    	if(table == null) {
    		throw new YbgException("0002");
    	} else {
    		sql.append(table.name()).append(" set ");
    	}
    	
    	List<Object> values = new ArrayList<Object>();
    	Field[]      fields = t.getClass().getDeclaredFields();
    	Field       idField = null;
    	
    	// 装拼需要保存的字段(有Column注解的字段才保存)
    	for(int i = 0; i < fields.length; i++) {
    		Id id = fields[i].getAnnotation(Id.class);
    		
    		if(id != null) {
    			idField = fields[i];
    			continue;
    		}
    		
    		Column column = fields[i].getAnnotation(Column.class);
    		
    		if(column != null) {
    			sql.append(column.name()).append(" = ?");
    			values.add(clazz.getMethod("get" + StringUtils.capitalize(fields[i].getName())).invoke(t));
    			
    			if(i < (fields.length - 1)) {
    				sql.append(", ");
    			}
    		}
    	}
    	
    	if(values.size() == 0) {
    		throw new YbgException("0003");
    	}
    	
    	// 若不存在主键则报错
    	if(idField == null) {
    		throw new YbgException("0004");
    	} else {
    		Column column = idField.getAnnotation(Column.class);
    		
    		if(column == null) {
    			throw new YbgException("0004");
    		} else {
    			sql.append(" where ").append(column.name()).append(" = ?");
    			values.add(clazz.getMethod("get" + StringUtils.capitalize(idField.getName())).invoke(t));
    		}
    	}
    	
    	return jdbcTemplate.update(sql.toString(), values.toArray()) == 1;
    }
    
    /**
     * 用于JDBCTemplate查询结果转成小写Key的Map&lt;String, Object&gt;
     */
    protected static class LowerCaseMapRowMapper implements RowMapper<Map<String, Object>> {
    	private Map<String, Object> newInstance(int columnCount) {
    		return new LinkedHashMap<>(columnCount);
    	}
    	
		@Override
		public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
			ResultSetMetaData rsMetaData = rs.getMetaData();
			int columnCount = rsMetaData.getColumnCount();

			Map<String, Object> map = newInstance(columnCount);
			for (int i = 1; i <= columnCount; i++) {
				String key = rsMetaData.getColumnName(i);
				Object valueObj = JdbcUtils.getResultSetValue(rs, i);
				map.put(key.toLowerCase(), valueObj);
			}
			return map;
		}
    }
    
    /**
     * 用于JDBCTemplate查询结果转成实体类
     */
    protected static class YbgRowMapper<T> implements RowMapper<T> {
    	private Class<T> clazz;
    	
    	public YbgRowMapper(Class<T> clazz) {
    		this.clazz = clazz;
    	}
    	
    	private T newInstance() {
			T t;
			
			try {
				t = clazz.newInstance();
			} catch (Exception e) {
				throw new YbgException(e);
			}
			
			return t;
    	}
    	
    	private String getSetMethodName(String name) {
    		return "set" + StringUtils.capitalize(name);
    	}
    	
		@Override
		public T mapRow(ResultSet rs, int rowNum) throws SQLException {
			T t = newInstance();
			
			Field[] fields = clazz.getDeclaredFields();
			for(Field field : fields) {
				Column column = field.getAnnotation(Column.class);
				
				if(column != null) {
					String   methodName = getSetMethodName(field.getName());
					Class<?> fieldType  = field.getType();
					Method   setMethod  = null;
					Object   parameter  = null;
					
					try {
						setMethod = clazz.getMethod(methodName, fieldType);
						
						if(fieldType == String.class) {
							parameter = rs.getString(column.name());
						} else if(fieldType == long.class || fieldType == Long.class) {
							parameter = rs.getLong(column.name());
						} else if(fieldType == int.class || fieldType == Integer.class) {
							parameter = rs.getInt(column.name());
						} else if(fieldType == Date.class) {
							Timestamp timestamp = rs.getTimestamp(column.name());
							if(timestamp != null) {
								parameter = new Date(timestamp.getTime());
							}
						} else if(fieldType == double.class || fieldType == Double.class) {
							parameter = rs.getDouble(column.name());
						} else if(fieldType == float.class || fieldType == Float.class) {
							parameter = rs.getFloat(column.name());
						} else if(fieldType == byte.class || fieldType == Byte.class || fieldType == char.class) {
							parameter = rs.getByte(column.name());
						} else if(fieldType == short.class || fieldType == Short.class) {
							parameter = rs.getShort(column.name());
						}
						
						if(parameter != null) setMethod.invoke(t, parameter);
					} catch(Exception e) {
						throw new YbgException(e);
					}
				}
			}
			
			return t;
		}
    }
    
    /**
     * 用于查询返回ID
     */
    protected static RowMapper<Long> idRowMapper = new RowMapper<Long>() {
		@Override
		public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
			return rs.getLong(1);
		}
    };
}
